﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace SPMS.Log.Appender
{
    /// <summary>
    /// 每日回转策略
    /// </summary>
    /// <remarks></remarks>
    /// <history>
    /// [zanezeng]               2009/10/10 14:52    创建
    /// </history>
    public sealed class DailyFileRollStrategy : IFileRollStrategy
    {
        #region ---IFileRollStrategy

        /// <summary>
        /// 回转日志文件
        /// </summary>
        /// <param name="currentFile">当前正在接受日志信息的文件</param>
        /// <param name="scheduledFileName">新的日志文件的文件名称</param>
        /// <returns>
        /// 	<c>true</c>：需要回转日志文件<br/>
        /// 	<c>false</c>：不需要回转日志文件
        /// </returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/10 14:41    创建
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/10 14:53    创建
        /// </history>
        public bool RollFile(FileStream currentFile, out string scheduledFileName)
        {
            //获得当前时间点
            DateTime currentDate = DateTime.Now;

            //设置初始值
            scheduledFileName = null;

            //用于记录是否需要回滚文件
            bool needRollFile = false;

            //判断日期是否发生变化
            if (currentDate.Year != this.m_PrevCheckedDate.Year ||
                currentDate.Month != this.m_PrevCheckedDate.Month ||
                currentDate.Day != this.m_PrevCheckedDate.Day)
            {
                //如果日期发生变化，则认为需要回滚
                needRollFile = true;
            }

            //判断当前文件是否为NULL
            if (null == currentFile)
            {
                //设置需呀回转文件
                needRollFile = true;
            }

            //回转文件
            if (needRollFile)
            {
                //保留本次检查的时间
                this.m_PrevCheckedDate = currentDate;

                //生成计划文件名称
                scheduledFileName = string.Format(System.Globalization.CultureInfo.InvariantCulture, this.ScheduledFileNamePattern, currentDate);

                //删除过期日志文件
                this.RemoveOverdueFiles(currentFile);
            }

            //返回
            return needRollFile;
        }

        #endregion

        #region ---Property

        /// <summary>
        /// 获得或者设置计划文件名称
        /// </summary>
        /// <value>计划文件名称</value>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/10 14:54    创建
        /// </history>
        public string ScheduledFileName
        {
            get
            {
                return this.m_ScheduledFileName;
            }
            set
            {
                //记录计划文件名称
                this.m_ScheduledFileName = value;

                //去掉空格和开头的\
                if (null != this.m_ScheduledFileName)
                {
                    //去掉空格
                    this.m_ScheduledFileName = this.m_ScheduledFileName.Trim();
                    //去掉开头的\
                    this.m_ScheduledFileName = this.m_ScheduledFileName.TrimStart('\\');
                }

                //清除计划文件名称模板
                this.m_ScheduledFileNamePattern = null;
            }
        }

        /// <summary>
        /// 获得或者设置日志的保留天数
        /// </summary>
        /// <value></value>
        /// <remarks>如果设置的保留天数小于等于0，则表示永不删除旧日志文件</remarks>
        /// <history>
        /// [zanezeng]               2009/10/10 15:54    创建
        /// </history>
        public int RetentionDays
        {
            get
            {
                return this.m_RetentionDays;
            }
            set
            {
                this.m_RetentionDays = value;
            }
        }

        /// <summary>
        /// 获得计划文件名称模板
        /// </summary>
        /// <value>计划文件名称模板</value>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/10 15:17    创建
        /// </history>
        private string ScheduledFileNamePattern
        {
            get
            {
                //判断计划文件名称模板是否生成
                if (null != this.m_ScheduledFileNamePattern)
                {
                    //如果已经生成，则直接返回
                    return this.m_ScheduledFileNamePattern;
                }

                //同步
                lock (this)
                {
                    //判断计划文件名称模板是否生成
                    if (null != this.m_ScheduledFileNamePattern)
                    {
                        //如果已经生成，则直接返回
                        return this.m_ScheduledFileNamePattern;
                    }

                    //判断计划文件名称是否设置
                    if (string.IsNullOrEmpty(this.ScheduledFileName))
                    {
                        //如果机会文件名称没有设置，则设置为默认的机会文件名称模板
                        this.m_ScheduledFileNamePattern = DEFAULT_SCHEDULEDFILENAMEPATTERN;

                        //设置文件匹配模板
                        this.m_FileMatchPattern = "Leyser_????????.log";

                    }
                    else
                    {
                        //获得目标名称
                        string directoryName = Path.GetDirectoryName(this.ScheduledFileName);

                        //获得扩展名
                        string extension = Path.GetExtension(this.ScheduledFileName);

                        //获得文件名称
                        string fileName = Path.GetFileNameWithoutExtension(this.ScheduledFileName);

                        //生成计划文件名称模板
                        if (string.IsNullOrEmpty(directoryName))
                        {
                            this.m_ScheduledFileNamePattern = fileName + "_{0:yyyyMMdd}" + extension;
                        }
                        else
                        {
                            this.m_ScheduledFileNamePattern = directoryName + @"\" + fileName + "_{0:yyyyMMdd}" + extension;
                        }

                        //设置文件匹配模板
                        this.m_FileMatchPattern = fileName + "_????????" + extension;
                    }

                    //判断计划文件名称模板是否为绝对路径
                    if (!Path.IsPathRooted(this.m_ScheduledFileNamePattern))
                    {
                        //将文件名称模板，组织为绝对文件路径
                        this.m_ScheduledFileNamePattern = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, this.m_ScheduledFileNamePattern);
                    }

                    //设置工作路径
                    this.m_WorkDirectory = Path.GetDirectoryName(this.m_ScheduledFileNamePattern);
                }

                //返回计划文件名称模板
                return this.m_ScheduledFileNamePattern;
            }
        }

        #endregion

        #region ---Private Method

        /// <summary>
        /// 删除过期的日志文件
        /// </summary>
        /// <param name="currentFile">当前正在接受日志信息的文件</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/10 16:02    创建
        /// </history>
        private void RemoveOverdueFiles(FileStream currentFile)
        {
            //判断是否永远保留
            if (this.RetentionDays <= 0)
            {
                //如果是永远保留，则直接返回
                return;
            }

            //判断是否设置工作路径和匹配模板
            if (string.IsNullOrEmpty(this.m_WorkDirectory) || string.IsNullOrEmpty(this.m_FileMatchPattern))
            {
                //如果没有工作路径和匹配模板，则直接返回
                return;
            }

            //用于保存日志文件
            string[] logFiles = null;

            //检索文件
            try
            {
                //判断文件夹是否存在
                if (Directory.Exists(this.m_WorkDirectory))
                {
                    //日志文件列表
                    logFiles = Directory.GetFiles(this.m_WorkDirectory, this.m_FileMatchPattern, SearchOption.TopDirectoryOnly);
                }
            }
            catch (Exception ex)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error(ex, "使用模板'{0}'在文件夹'{1}'检索现有的日志文件失败", this.m_FileMatchPattern, this.m_WorkDirectory);
                }

                //直接返回
                return;
            }

            //判断是否需要删除日志文件
            if (null == logFiles || logFiles.Length <= this.RetentionDays)
            {
                //如果不需要删除，则直接返回
                return;
            }

            //计算要保留日志文件的最早日期
            DateTime preservedDate = DateTime.Now.AddDays(this.RetentionDays * -1);

            //计算要保留日志文件的日期值
            int preservedDateValue = preservedDate.Year * 10000 + preservedDate.Month * 100 + preservedDate.Day;

            //用于保存日志文件名称
            string fileName;

            //用于保存匹配结果
            Match match;

            //循环删除旧的日志文件
            foreach (string filePath in logFiles)
            {
                //判断是否为当前正打开的文件
                if (null != currentFile && 0 == string.Compare(filePath, currentFile.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    //如果要删除的文件为当前正打开的文件，则忽略
                    continue;
                }

                //获得日志文件名称
                fileName = Path.GetFileNameWithoutExtension(filePath);

                //匹配日志文件的日期
                match = Regex.Match(filePath, PATTERN_FILEDATE, RegexOptions.IgnoreCase);

                //判断是否匹配
                if (!match.Success)
                {
                    //如果不匹配，则跳过
                    continue;
                }

                //判断日志是否需要删除，早于保留日期以前的日志全部都需要删除
                if (int.Parse(match.Value) < preservedDateValue)
                {
                    try
                    {
                        //如果需要删除，则删除日志文件
                        SPMS.Utility.IoUtility.DeleteFile(filePath);
                    }
                    catch (Exception ex)
                    {
                        //输出日志
                        if (LoggingContext.MetaLog.IsErrorEnabled)
                        {
                            LoggingContext.MetaLog.Error(ex, "删除过期日志文件'{0}'失败", filePath);
                        }
                    }
                }
            }
        }

        #endregion

        #region ---Field

        /// <summary>
        /// 计划文件名称
        /// </summary>
        private string m_ScheduledFileName;

        /// <summary>
        /// 日志的工作目录
        /// </summary>
        private string m_WorkDirectory;

        /// <summary>
        /// 文件匹配模板
        /// </summary>
        private string m_FileMatchPattern;

        /// <summary>
        ///日志文件保留的天数
        /// </summary>
        private int m_RetentionDays = DEFAULT_RETENTIONDAYS;

        /// <summary>
        /// 默认的保留天数
        /// </summary>
        private const int DEFAULT_RETENTIONDAYS = 30;

        /// <summary>
        /// 计划文件名称模板
        /// </summary>
        private string m_ScheduledFileNamePattern;

        /// <summary>
        /// 默认的机会文件名称模板
        /// </summary>
        private const string DEFAULT_SCHEDULEDFILENAMEPATTERN = "Leyser_{0:yyyyMMdd}.log";

        /// <summary>
        /// 日志文件日志匹配模板
        /// </summary>
        private const string PATTERN_FILEDATE = @"\d{8,8}";

        /// <summary>
        /// 上一次检查的时间
        /// </summary>
        private DateTime m_PrevCheckedDate = DateTime.Now;

        #endregion
    }
}
